<template>
  <div class="texture-cube">
    <h1>纹理贴图</h1>
    <p>
      在WebGL中，纹理贴图是一种将图像映射到3D物体表面的技术。通过纹理贴图，我们可以为3D模型添加丰富的表面细节，使其更加真实。
    </p>

    <el-card class="demo-section">
      <template #header>
        <div class="card-header">
          <h3>纹理贴图示例</h3>
        </div>
      </template>
      <div class="demo-content">
        <canvas ref="canvas" width="400" height="400"></canvas>
        <div class="control-panel">
          <el-form :model="textureForm" label-width="120px">
            <el-form-item label="纹理重复方式">
              <el-select v-model="textureForm.wrapMode" @change="updateTextureParams">
                <el-option label="重复" value="REPEAT" />
                <el-option label="镜像重复" value="MIRRORED_REPEAT" />
                <el-option label="边缘拉伸" value="CLAMP_TO_EDGE" />
              </el-select>
            </el-form-item>
            <el-form-item label="纹理过滤方式">
              <el-select v-model="textureForm.filterMode" @change="updateTextureParams">
                <el-option label="最近点采样" value="NEAREST" />
                <el-option label="线性过滤" value="LINEAR" />
              </el-select>
            </el-form-item>
            <el-form-item label="深度测试">
              <el-switch v-model="textureForm.depthTest" @change="updateDepthTest" />
            </el-form-item>
          </el-form>
          <p>这是一个带有纹理贴图的旋转立方体，你可以：</p>
          <ul>
            <li>调整纹理的重复方式</li>
            <li>修改纹理的过滤方式</li>
            <li>开启/关闭深度测试</li>
          </ul>
        </div>
      </div>
    </el-card>

    <el-card class="code-section">
      <template #header>
        <div class="card-header">
          <h3>示例代码</h3>
        </div>
      </template>
      <div class="code-content">
        <pre><code>
// 顶点着色器
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;

void main() {
  gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
  v_TexCoord = a_TexCoord;
}

// 片元着色器
precision mediump float;
varying vec2 v_TexCoord;
uniform sampler2D u_Sampler;

void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}

// JavaScript代码
const canvas = document.getElementById('webgl');
const gl = canvas.getContext('webgl');

// 创建着色器程序...

// 设置顶点位置和纹理坐标
const vertices = new Float32Array([
  // 顶点坐标和纹理坐标
  -0.5, -0.5,  0.5,  0.0, 0.0,  // 前面
   0.5, -0.5,  0.5,  1.0, 0.0,
   0.5,  0.5,  0.5,  1.0, 1.0,
  -0.5,  0.5,  0.5,  0.0, 1.0,
  // 更多顶点数据...
]);

// 加载纹理
function loadTexture(gl, url) {
  const texture = gl.createTexture();
  const image = new Image();
  image.onload = function() {
    if (!gl || !texture) return

    gl.bindTexture(gl.TEXTURE_2D, texture)
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image as HTMLImageElement)

    // 设置纹理参数
    const wrapMode = textureForm.wrapMode === 'REPEAT' ? gl.REPEAT :
      textureForm.wrapMode === 'MIRRORED_REPEAT' ? gl.MIRRORED_REPEAT : gl.CLAMP_TO_EDGE
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode)

    const filterMode = textureForm.filterMode === 'NEAREST' ? gl.NEAREST : gl.LINEAR
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode)
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, filterMode)

    // 生成mipmap
    gl.generateMipmap(gl.TEXTURE_2D)
  }
  image.src = url
}
        </code></pre>
      </div>
    </el-card>
  </div>
</template>

<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { WebGLUtils } from '../../utils/webgl'

const canvas = ref<HTMLCanvasElement | null>(null)

// 响应式表单数据
const textureForm = reactive({
  wrapMode: 'REPEAT',
  filterMode: 'LINEAR',
  depthTest: true
})

// 更新深度测试状态
const updateDepthTest = () => {
  if (!canvas.value) return
  const gl = canvas.value.getContext('webgl')
  if (!gl) return

  if (textureForm.depthTest) {
    gl.enable(gl.DEPTH_TEST)
  } else {
    gl.disable(gl.DEPTH_TEST)
  }
}

// 更新纹理参数
const updateTextureParams = () => {
  if (!canvas.value || !texture) return
  const gl = canvas.value.getContext('webgl')
  if (!gl) return

  gl.bindTexture(gl.TEXTURE_2D, texture)

  // 由于现在是2的幂次方纹理，可以使用所有环绕方式
  const wrapMode = textureForm.wrapMode === 'REPEAT' ? gl.REPEAT :
    textureForm.wrapMode === 'MIRRORED_REPEAT' ? gl.MIRRORED_REPEAT :
    gl.CLAMP_TO_EDGE

  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode)

  // 可以使用 MIPMAP
  const filterMode = textureForm.filterMode === 'NEAREST' ? gl.NEAREST_MIPMAP_NEAREST : gl.LINEAR_MIPMAP_LINEAR
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filterMode)
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, textureForm.filterMode === 'NEAREST' ? gl.NEAREST : gl.LINEAR)
}

// 顶点着色器
const vertexShaderSource = `
attribute vec4 a_Position;
attribute vec2 a_TexCoord;
varying vec2 v_TexCoord;
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjMatrix;

void main() {
  gl_Position = u_ProjMatrix * u_ViewMatrix * u_ModelMatrix * a_Position;
  v_TexCoord = a_TexCoord;
}
`

// 片元着色器
const fragmentShaderSource = `
precision mediump float;
varying vec2 v_TexCoord;
uniform sampler2D u_Sampler;

void main() {
  gl_FragColor = texture2D(u_Sampler, v_TexCoord);
}
`

// 立方体的顶点坐标和纹理坐标
const vertices = new Float32Array([
  // 前面
  -0.5, -0.5,  0.5,  0.0, 0.0,  // 左下
   0.5, -0.5,  0.5,  1.0, 0.0,  // 右下
   0.5,  0.5,  0.5,  1.0, 1.0,  // 右上
  -0.5,  0.5,  0.5,  0.0, 1.0,  // 左上
  // 后面
  -0.5, -0.5, -0.5,  1.0, 0.0,
  -0.5,  0.5, -0.5,  1.0, 1.0,
   0.5,  0.5, -0.5,  0.0, 1.0,
   0.5, -0.5, -0.5,  0.0, 0.0,
  // 顶面
  -0.5,  0.5, -0.5,  0.0, 0.0,
  -0.5,  0.5,  0.5,  0.0, 1.0,
   0.5,  0.5,  0.5,  1.0, 1.0,
   0.5,  0.5, -0.5,  1.0, 0.0,
  // 底面
  -0.5, -0.5, -0.5,  0.0, 1.0,
   0.5, -0.5, -0.5,  1.0, 1.0,
   0.5, -0.5,  0.5,  1.0, 0.0,
  -0.5, -0.5,  0.5,  0.0, 0.0,
  // 右面
   0.5, -0.5, -0.5,  0.0, 0.0,
   0.5,  0.5, -0.5,  1.0, 0.0,
   0.5,  0.5,  0.5,  1.0, 1.0,
   0.5, -0.5,  0.5,  0.0, 1.0,
  // 左面
  -0.5, -0.5, -0.5,  1.0, 0.0,
  -0.5, -0.5,  0.5,  0.0, 0.0,
  -0.5,  0.5,  0.5,  0.0, 1.0,
  -0.5,  0.5, -0.5,  1.0, 1.0
])

// 顶点索引
const indices = new Uint8Array([
  0,  1,  2,   0,  2,  3,  // 前面
  4,  5,  6,   4,  6,  7,  // 后面
  8,  9,  10,  8,  10, 11, // 顶面
  12, 13, 14,  12, 14, 15, // 底面
  16, 17, 18,  16, 18, 19, // 右面
  20, 21, 22,  20, 22, 23  // 左面
])

// 纹理对象
let texture: WebGLTexture | null = null

// 修改 loadTexture 函数
function loadTexture(gl: WebGLRenderingContext, program: WebGLProgram, url: string) {
  texture = gl.createTexture()
  if (!texture) {
    console.error('创建纹理失败')
    return
  }

  // 创建临时纹理
  gl.bindTexture(gl.TEXTURE_2D, texture)
  gl.texImage2D(
    gl.TEXTURE_2D,
    0,
    gl.RGBA,
    1,
    1,
    0,
    gl.RGBA,
    gl.UNSIGNED_BYTE,
    new Uint8Array([255, 0, 255, 255])
  )

  const image = new Image()
  image.crossOrigin = 'anonymous'

  image.onload = function() {
    console.log('纹理图片加载成功')
    if (!gl || !texture) return

    // 创建临时画布来调整图片尺寸
    const canvas = document.createElement('canvas')
    // 将尺寸调整为2的幂次方
    const size = Math.pow(2, Math.ceil(Math.log2(Math.max(image.width, image.height))))
    canvas.width = size
    canvas.height = size
    const ctx = canvas.getContext('2d')
    if (!ctx) return

    // 在画布上绘制图片，这会自动调整尺寸
    ctx.drawImage(image, 0, 0, size, size)

    gl.bindTexture(gl.TEXTURE_2D, texture)
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, canvas)

    // 对于2的幂次方纹理，我们可以启用 mipmap
    gl.generateMipmap(gl.TEXTURE_2D)

    // 更新纹理参数
    updateTextureParams()

    // 激活纹理单元并绑定纹理
    gl.activeTexture(gl.TEXTURE0)
    gl.bindTexture(gl.TEXTURE_2D, texture)
    gl.uniform1i(gl.getUniformLocation(program, 'u_Sampler'), 0)
  }

  image.onerror = function(err) {
    console.error('纹理图片加载失败:', url, err)
  }

  image.src = url
}

// 在 onMounted 中的修改
onMounted(() => {
  if (!canvas.value) return

  const gl = canvas.value.getContext('webgl')
  if (!gl) return

  // 创建WebGL工具实例
  const webglUtils = new WebGLUtils(gl)

  // 创建着色器
  const vertexShader = webglUtils.createShader(gl.VERTEX_SHADER, vertexShaderSource)
  const fragmentShader = webglUtils.createShader(gl.FRAGMENT_SHADER, fragmentShaderSource)
  if (!vertexShader || !fragmentShader) return

  // 创建程序并链接着色器
  const program = webglUtils.createProgram(vertexShader, fragmentShader)
  if (!program) return

  gl.useProgram(program)

  // 获取变量位置
  const a_Position = gl.getAttribLocation(program, 'a_Position')
  const a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord')
  const u_ModelMatrix = gl.getUniformLocation(program, 'u_ModelMatrix')
  const u_ViewMatrix = gl.getUniformLocation(program, 'u_ViewMatrix')
  const u_ProjMatrix = gl.getUniformLocation(program, 'u_ProjMatrix')

  // 创建缓冲区
  const vertexBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

  const indexBuffer = gl.createBuffer()
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)

  // 设置顶点属性
  gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 20, 0)
  gl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 20, 12)
  gl.enableVertexAttribArray(a_Position)
  gl.enableVertexAttribArray(a_TexCoord)

  // 设置视图矩阵
  const viewMatrix = new Float32Array([
    1.0, 0.0, 0.0, 0.0,
    0.0, 1.0, 0.0, 0.0,
    0.0, 0.0, 1.0, 0.0,
    0.0, 0.0, -5.0, 1.0
  ])

  // 设置投影矩阵（透视投影）
  const fov = 45 * Math.PI / 180
  const aspect = canvas.value.width / canvas.value.height
  const near = 0.1
  const far = 100.0
  const f = 1.0 / Math.tan(fov / 2)

  const projMatrix = new Float32Array([
    f / aspect, 0.0, 0.0, 0.0,
    0.0, f, 0.0, 0.0,
    0.0, 0.0, (far + near) / (near - far), -1.0,
    0.0, 0.0, (2 * far * near) / (near - far), 0.0
  ])

  // 在创建程序后立即初始化纹理
  gl.useProgram(program)

  // 启用深度测试
  gl.enable(gl.DEPTH_TEST)

  // 加载纹理
  const textureUrl = '/texture/pige.jpg'  // 确保图片在 public/texture 目录下
  loadTexture(gl, program, textureUrl)

  // 设置初始模型矩阵
  let angle = 0
  const modelMatrix = new Float32Array(16)

  // 动画函数
  function animate() {
    if (!gl || !texture) {
      requestAnimationFrame(animate)
      return
    }

    angle = (angle + 1) % 360
    const radian = angle * Math.PI / 180

    // 设置旋转矩阵
    modelMatrix[0] = Math.cos(radian)
    modelMatrix[1] = 0
    modelMatrix[2] = -Math.sin(radian)
    modelMatrix[3] = 0
    modelMatrix[4] = 0
    modelMatrix[5] = 1
    modelMatrix[6] = 0
    modelMatrix[7] = 0
    modelMatrix[8] = Math.sin(radian)
    modelMatrix[9] = 0
    modelMatrix[10] = Math.cos(radian)
    modelMatrix[11] = 0
    modelMatrix[12] = 0
    modelMatrix[13] = 0
    modelMatrix[14] = 0
    modelMatrix[15] = 1

    // 清除画布
    gl.clearColor(0.0, 0.0, 0.0, 1.0)
    if (textureForm.depthTest) {
      gl.enable(gl.DEPTH_TEST)
    } else {
      gl.disable(gl.DEPTH_TEST)
    }
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

    // 激活并绑定纹理
    gl.activeTexture(gl.TEXTURE0)
    gl.bindTexture(gl.TEXTURE_2D, texture)
    gl.uniform1i(gl.getUniformLocation(program!, 'u_Sampler'), 0)

    // 绘制立方体
    gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix)
    gl.uniformMatrix4fv(u_ViewMatrix, false, viewMatrix)
    gl.uniformMatrix4fv(u_ProjMatrix, false, projMatrix)
    gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_BYTE, 0)

    requestAnimationFrame(animate)
  }

  // 启动动画
  animate()
})
</script>

<style lang="scss" scoped>
.texture-cube {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;

  h1 {
    color: #303133;
    margin-bottom: 20px;
  }

  .demo-section,
  .code-section {
    margin: 30px 0;

    .card-header {
      display: flex;
      align-items: center;

      h3 {
        margin: 0;
        color: #409eff;
      }
    }
  }

  .demo-section {
    .demo-content {
      display: flex;
      gap: 20px;

      canvas {
        border: 1px solid #dcdfe6;
        background-color: #000;
      }

      .control-panel {
        flex: 1;
        padding: 20px;
        background-color: #f8f9fa;
        border-radius: 4px;

        ul {
          list-style-type: none;
          padding-left: 0;

          li {
            margin: 8px 0;
          }
        }
      }
    }
  }

  .code-section {
    .code-content {
      background-color: #f8f9fa;
      padding: 15px;
      border-radius: 4px;

      code {
        font-family: monospace;
        color: #409eff;
      }

      pre {
        margin: 0;
        white-space: pre-wrap;
      }
    }
  }
}
</style>
